Skip to content

feat(contexts): save the menu of an application - #3136

Closed
rubenvdlinde wants to merge 4 commits into
feat/noid/app-menu-modelfrom
feat/noid/app-menu-saving
Closed

rubenvdlinde wants to merge 4 commits into
feat/noid/app-menu-modelfrom
feat/noid/app-menu-saving

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 22, 2026 •

Copy link
Copy Markdown

Part of #3099, on top of #3135 (menu model). Creating and updating an application can now include its menu. This replaces #3087 by @remko48.

Size: 1543 lines: 590 code, 839 tests and 114 generated OpenAPI.

What changes

  • Saving replaces the whole menu, in the same transaction as the application.
  • Links must be http or https, without spaces or control characters.
  • At most 200 items per application.
  • A page target must be a page of the same application, otherwise 400. When you create an application, a page item without a page id means its new start page.
  • The default menu. If you create an application without sending a menu, it gets one item, "Dashboard", that opens the start page. If you do send a menu, even an empty one, it's stored as you sent it. As Grid pages in applications (frontend) #3099 says, the Dashboard item is an ordinary item: it can be renamed, moved or removed. This differs from what we wrote on feat(views): allow views without a table #3075.
  • A stored item whose page is gone stays in the menu and stays marked as broken when you save other changes. Only a new item that points at a missing or foreign page is refused.
  • Tables and views in the menu become part of the application, after the same permission check as adding them to the application directly. A table that's both in nodes and in the menu is added once.
  • The create and update responses keep exactly the shape they have on main; only menuItems and the new page fields are added.
  • Invalid input when creating an application now gives a 400 instead of a 500.

OpenAPI and the TypeScript types are regenerated.

How it was tested

On Nextcloud 34 with PostgreSQL:

  • MenuItemInputTest: labels, links, targets, technical names
  • ContextServiceTest: saving, the default Dashboard item, page targets, broken items kept, the item_<id> default
  • ContextControllerTest and ContextPagesRoundTripTest (database: a table in both nodes and the menu is added once, on create and on update; the response shape stays as on main)
  • the full unit suite on the top of the stack (feat(contexts): carry the menu through export and import #3138), on PostgreSQL and SQLite: 684 tests
  • composer cs:check, psalm and rector, all clean

🖼️ Screenshots

Backend only, no UI changes.

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@rubenvdlinde
rubenvdlinde added this pull request to stack #3139 October 22, 2026 11:42
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 9847aa9 to 5825db2 Compare October 22, 2026 13:25
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-saving branch from 6540b64 to b167dd6 Compare October 22, 2026 13:25
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 5825db2 to 9e81a78 Compare October 23, 2026 08:05
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-saving branch 2 times, most recently from 85b998e to 9247115 Compare October 24, 2026 08:09
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 9e81a78 to 7e236cc Compare October 24, 2026 08:09
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-saving branch 2 times, most recently from b99771d to 86bf312 Compare October 26, 2026 09:24
@rubenvdlinde
rubenvdlinde marked this pull request as ready for review October 26, 2026 09:25

@enjeck enjeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment on tests as in previous PRs. There are too many and it's hard to tell whcih are actualy useful

Comment on lines +322 to +323
$this->menuItemMapper->deleteAllByContextId($context->getId());
$this->insertMenuItems($context->getId(), $menuItems, $keptNames);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like a flawed design if each save deletes the menu and inserts it again

* @param list<array<array-key, mixed>> $storedMenu
* @throws Exception|PermissionError
*/
private function addMenuTargetsAsNodes(Context $context, array $menuItems, array $storedMenu, ?string $userId): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if we can remove this function if menu items point at node_rel_id instead 🤔 . It seems too complicated

Comment on lines +517 to +518
// A transaction that reads before it writes can lose its write lock to a concurrent
// request (SQLite answers "database is locked" at once); running it again succeeds.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unrelated and should be a separate PR?

@rubenvdlinde
rubenvdlinde marked this pull request as draft November 2, 2026 11:14
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from c7a6e95 to 8574da7 Compare November 2, 2026 11:17
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-saving branch 2 times, most recently from 3593dbc to 9c7797a Compare November 2, 2026 11:23
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 8574da7 to 9af2352 Compare November 2, 2026 11:23
rubenvdlinde and others added 4 commits November 3, 2026 15:51
A menu item has a label, an optional icon and description, and opens a
table, a view, a page of the same application or a link. Items have a
technical name unique within the application (item_<id> by default)
and may sit under another item. An item whose target is gone is read
as broken. Applications carry their menu in their responses, loaded
for all applications of a response in one query.

Deleting an application deletes its menu, and deletes the application
row first inside a retried transaction.

Co-authored-by: remko48 <remko@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
Creating and updating an application can include its menu, saved as a
whole. Links must be http or https, page targets must be pages of the
same application, and tables and views in the menu become part of the
application. A new application without a submitted menu gets a
Dashboard item that opens its start page.

Co-authored-by: remko48 <remko@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
Assisted-by: ClaudeCode:claude-opus-5-5
Signed-off-by: Ruben van der Linde <ruben@conduction.nl>
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-saving branch from 9c7797a to eb6d3f4 Compare November 3, 2026 14:51
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 9af2352 to 7d89fa5 Compare November 3, 2026 14:51
@rubenvdlinde
rubenvdlinde removed this pull request from stack #3139 November 4, 2026 10:41
@rubenvdlinde
rubenvdlinde force-pushed the feat/noid/app-menu-model branch from 7d89fa5 to aa0c7e3 Compare November 4, 2026 10:42
@rubenvdlinde

Copy link
Copy Markdown
Author

Replaced by the rebuilt #3135 and #3138, which follow the proposal on #3100.

@rubenvdlinde
rubenvdlinde deleted the feat/noid/app-menu-saving branch November 4, 2026 12:36
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Hello there,
Thank you so much for taking the time and effort to create a pull request to our Nextcloud project.

We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process.

Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6

Thank you for contributing to Nextcloud and we hope to hear from you soon!

(If you believe you should not receive this message, you can add yourself to the blocklist.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants